package cn.zifangsky.random.questions;

import org.junit.Test;

import java.math.BigDecimal;
import java.util.Random;

/**
 * 抢红包算法
 *
 * @author zifangsky
 * @date 2020/5/11
 * @since 1.0.0
 */
public class Problem_003_Grab_The_Red_Envelope {

    /**
     * 测试代码
     */
    @Test
    public void testMethods(){
        Solution test1 = new Solution(new BigDecimal(50), 15);
        for(int i = 0; i < 15; i++){
            System.out.print(test1.nextRedEnvelope() + " ");
        }
        System.out.println();


        Solution test2 = new Solution(new BigDecimal(0.2), 20);
        for(int i = 0; i < 20; i++){
            System.out.print(test2.nextRedEnvelope() + " ");
        }
    }



    static class Solution{
        /**
         * 剩余金额
         */
        private int restMoney;
        /**
         * 剩余人数
         */
        private int restNum;

        private Random rnd;

        Solution(BigDecimal totalMoney, int totalNum){
            if(totalMoney.doubleValue() <= 0 || totalNum < 1){
                throw new IllegalArgumentException("红包总金额或者抢红包总人数存在异常！");
            }

            this.restMoney = totalMoney.multiply(new BigDecimal(100)).intValue();
            this.restNum = totalNum;

            if(this.restMoney < this.restNum){
                throw new IllegalArgumentException("红包总金额不足每人1分钱！");
            }

            this.rnd = new Random();
        }

        /**
         * 抢红包
         */
        public synchronized BigDecimal nextRedEnvelope(){
            if(this.restNum < 1){
                throw new IllegalStateException("红包已经被抢光了！");
            }

            //1. 如果剩余人数为1，则直接返回
            if(this.restNum == 1){
                return this.format(this.restMoney);
            }

            //2. 开始抢红包
            //2.1 计算剩余平均值
            int averageMoney = this.restMoney / this.restNum;

            //2.2 当前红包金额最大值：Min(averageMoney * 2, (restMoney - (restNum - 1)))，目的是为了保障剩下的人至少可以抢到1分钱
            int currentMaxMoney = Math.min((averageMoney * 2), (this.restMoney - (this.restNum - 1)));

            //2.3 计算当前红包金额（最少可以抢到1分钱）
            int money = Math.max(rnd.nextInt(currentMaxMoney), 1);

            this.restMoney -= money;
            this.restNum--;

            return this.format(money);
        }

        /**
         * 将抢到的红包转换回以元为单位的金额
         */
        private BigDecimal format(int money){
            return new BigDecimal(money).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
        }
    }

}
